﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Threading;
using Algobox.Objects.Finance;

namespace Algobox.Feeds.Finance.Simulators
{
    public class TradingSimulator : Algobox.Feeds.Finance.Trading.ITradingFeedAdaptor
    {
        #region Constructors

        public TradingSimulator()
        {
            _feedName = typeof(TradingSimulator).Name + System.Threading.Interlocked.Increment(ref _instanceCounter);
        }

        #endregion
//////////////////
        #region Member Variables

        public event System.Action OnTradingFeedShutdown;

        string _feedName;
        static long _instanceCounter;
        
        private ConcurrentDictionary<string, OrderCallback> _mapOrderEvents = new ConcurrentDictionary<string, OrderCallback>();

        private class OrderCallback
        {
            public OrderCallback(Objects.Finance.Orders.Order order, Feeds.Finance.Trading.IOrderEvent callback)
            {
                Order = order;
                Callback = callback;
            }
            public Objects.Finance.Orders.Order Order;
            public Feeds.Finance.Trading.IOrderEvent Callback;
        }

        #endregion
//////////////////
        #region Properties

        public string FeedName
        {
            get { return _feedName; }
        }

        public bool IsStarted { get; private set; }


        #endregion
//////////////////
        #region Functions
        
        public bool Start()
        {
            IsStarted = true;
            return true;
        }

        public void Stop()
        {
            IsStarted = false;
        }

        public bool TrySubmitOrder(Objects.Finance.Orders.Order order, Trading.IOrderEvent callback, out string message)
        {
            OrderCallback oc = new OrderCallback(order, callback);
            if (_mapOrderEvents.TryAdd(order.OrderIdClient, oc))
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(AddOrder), oc);
                message = "PendingNew";
                return true;
            }
            message = "AlreadyAdded";
            return false;
        }

        public bool TrySubmitOrder(Objects.Finance.Orders.Order order, Trading.IOrderEvent callback, Exchanges.Destinations dest, out string message)
        {
            OrderCallback oc = new OrderCallback(order, callback);
            if (_mapOrderEvents.TryAdd(order.OrderIdClient, oc))
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(AddOrder), oc);
                message = "PendingNew";
                return true;
            }
            message = "AlreadyAdded";
            return false;
        }

        public bool TryReplaceOrder(Objects.Finance.Orders.Order order, out string message, decimal newPrice = 0m, uint newQuantity = 0)
        {
            OrderCallback callback;
            if (_mapOrderEvents.TryRemove(order.OrderIdClient, out callback))
            {
                callback.Order.Price = newPrice;
                callback.Order.Quantity = newQuantity;
                ThreadPool.QueueUserWorkItem(new WaitCallback(ReplaceOrder), callback);
                message = "Pendingreplace";
                return true;
            }
            message = "OrderNotFound";
            return false;
        }

        public bool TryCancelOrder(Objects.Finance.Orders.Order order, out string message)
        {
            OrderCallback callback;
            if (_mapOrderEvents.TryRemove(order.OrderIdClient, out callback))
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(CancelOrder), callback);
                message = "PendingCancel";
                return true;
            }
            message = "OrderNotFound";
            return false;
        }
        
        public bool TryGetOrder(string clientOrderId, out Objects.Finance.Orders.Order order)
        {
            throw new NotImplementedException();
        }
        
        private void AddOrder(object paramater)
        {
            OrderCallback oc = paramater as OrderCallback;
            if (oc != null)
            {
                oc.Order.TimeUpdated = oc.Order.TimeCreated;
                oc.Order.QuantityFilled = oc.Order.Quantity;
                oc.Order.QuantityRemaining = 0;
                oc.Order.PriceAverage = oc.Order.Price;
                oc.Callback.OrderUpdate(oc.Order, Objects.Finance.Orders.Order.Status.Filled);
            }
        }

        private void ReplaceOrder(object paramater)
        {
            OrderCallback oc = paramater as OrderCallback;
            if (oc != null)
            {
                oc.Order.TimeUpdated = oc.Order.TimeCreated;
                oc.Callback.OrderUpdate(oc.Order, Objects.Finance.Orders.Order.Status.Replaced);
            }
        }

        private void CancelOrder(object paramater)
        {
            OrderCallback oc = paramater as OrderCallback;
            if (oc != null)
            {
                oc.Order.TimeUpdated = oc.Order.TimeCreated;
                oc.Callback.OrderUpdate(oc.Order, Objects.Finance.Orders.Order.Status.Canceled);
            }
        }

        #endregion
//////////////////
        #region Enums



        #endregion


    }
}
